/*
 * Jenny Ruppen 09-730-391
 * Sebastien Goldberg 08-914-855
 * Henry Raymond 08-928-061
 * Stefan Mussato 09-752-270
 */
 
package ch.uzh.ifi.sopra.gruppe9.game;

import java.net.InetAddress;
import java.net.MalformedURLException;
import java.rmi.ConnectException;
import java.rmi.Naming;
import java.rmi.NotBoundException;
import java.rmi.RemoteException;
import java.rmi.UnknownHostException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;


/**
 * This static class takes care of connecting the client to the server. It also looks up all
 * remote objects needed by the client.
 */
public class Connection {
	
	
	/**
	 * RMI registry used to look up remote objects
	 */
	private static Registry r;
	
	
	/**
	 * IP address of connection
	 */
	private static String ip;
	
	
	/**
	 * Virtual game board for RMI methods
	 */
	private static GameBoardStub gameBoard;
	
	
	/**
	 * Index of player on server
	 */
	private static int myIndex;
	
	
	/**
	 * Error types.
	 */
	public enum Error {COULD_NOT_CONNECT, FOUR_PLAYERS, GAME_STARTED};
	
	
	/**
	 * Error type in case connection was unsuccessful.
	 */
	private static Error error;
	
	
	/**
	 * Client-side subscriber to updates coming from server.
	 */
	private static UpdateSubscriber updateSubscriber;
	
	
	/**
	 * Player becomes server or connects to existing server.
	 */
	public static void connect(String newIp) {
		ip = newIp;
		
		try {
			try {
				// Try being client
				r = LocateRegistry.getRegistry(ip);
				r.lookup("gameBoard"); // provoke exception
				
				try {
					// Test if playing locally
					Registry rLocal = LocateRegistry.getRegistry(getMyIp());
					rLocal.lookup("gameBoard"); // provoke exception
				} catch (ConnectException e) {
					// Create local server for update subscriber
					UpdateSubscriberServer.createInstance();
				}
				
			} catch (ConnectException e) {
				// Be server
				Server.createInstance();
				r = LocateRegistry.getRegistry(ip);
			}
			lookup();
			subscribeToServerUpdates();
			updatePlayerList();
			
			GUI.changeState(GuiState.State.GAME_SETUP_SCREEN);
			
		} catch (UnknownHostException e) { // Could not connect to IP
			error = Error.COULD_NOT_CONNECT;
			GUI.changeState(GuiState.State.CONNECTION_FAILURE);
			
		} catch (NotBoundException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		if(myIndex == -1) { // Already 4 players on server
			error = Error.FOUR_PLAYERS;
			GUI.changeState(GuiState.State.CONNECTION_FAILURE);
		}
		if(myIndex == -2) { // The game already started
			error = Error.GAME_STARTED;
			GUI.changeState(GuiState.State.CONNECTION_FAILURE);
		}
	}
	
	
	/**
	 * Look up remote objects in the RMI registry
	 */
	private static void lookup() throws NotBoundException, RemoteException {
		IRmiGameBoard rmiGameBoard = (IRmiGameBoard)r.lookup("gameBoard");
		gameBoard = new GameBoardStub(rmiGameBoard);
		gameBoard.lookupActionLocations(r);
		myIndex = gameBoard.registerPlayer();
		gameBoard.setMyIndex(myIndex);
		Game.setGameBoard(gameBoard);
	}
	
	
	/**
	 * Sets up client-side update subscriber and registers it with server.
	 */
	private static void subscribeToServerUpdates() {
		try {
			updateSubscriber = new UpdateSubscriber();
			Naming.rebind("updateSubscriber" + myIndex, (IRmiUpdateSubscriber) updateSubscriber);
			gameBoard.registerUpdateSubscriber("updateSubscriber" + myIndex, getMyIp());
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Adds new player RMI objects, if new players joined the server.
	 */
	public static void updatePlayerList() {
		try {
			gameBoard.lookupPlayers(r);
		} catch (NotBoundException e) {
			e.printStackTrace();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * Get IP address of current connection
	 */
	public static String getIP() {
		return ip;
	}
	
	
	/**
	 * Get virtual game board for RMI methods
	 */
	public static GameBoardStub getGameBoard() {
		return gameBoard;
	}
	
	
	/**
	 * Get index of player on server
	 */
	public static int getMyIndex() {
		return myIndex;
	}


	/**
	 * Returns error type in case connection was unsuccessful.
	 */
	public static Error getError() {
		return error;
	}
	
	
	/**
	 * Returns the IP of this machine
	 */
	public static String getMyIp() {
		String ip = "";
		try {
			ip = InetAddress.getLocalHost().getHostAddress();
		} catch (java.net.UnknownHostException e) {
			e.printStackTrace();
		}
		return ip;
	}
	
}
